
import log from '../../logutil';
import systemDateTime from '@ohos.systemDateTime';
import cryptoFramework from '@ohos.security.cryptoFramework';
import util from '@ohos.util';

import apiMagr from '../apiMagr';
import api from '../api';
import http from '@ohos.net.http';
import { DEFAULT } from '@ohos/hypium';
import deviceInfo from '@ohos.deviceInfo';
import getFp from './genshin/getFp';
import kvStoreUtil from '../../kvStore/kvStoreUtil';
import dailyNoteApi from './genshin/dailyNote';

/**
 * mys相关Api管理类
 */
export default class mysApiMagr extends apiMagr{
  private salt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs"
  private Cookies:Map<string,string>
  private static devicefp
  public static async getInstance():Promise<mysApiMagr>{
    let cookies = await kvStoreUtil.getInstance().get("MHY-Cookies")
    try{
      let devicefp = await kvStoreUtil.getInstance().get("MHY-DeviceFp")
      this.devicefp = devicefp
    }catch (e){
      log.warn(mysApiMagr.name,JSON.stringify(e))
    }
    if(cookies !== null && cookies !== undefined){
      try{
        return new mysApiMagr(cookies)
      }catch (e){
        return Promise.reject(JSON.stringify(e))
      }
    }
  }
  /*
   * @param cookies 用户mys的Cookies，字符串类型或Map<string,string>类型
   */
  protected constructor(cookies:string|Map<string,string>) {
    super();
    if(typeof cookies !== "string"){
      if(cookies instanceof Map){
        this.Cookies = cookies;
        return;
      }else{
        throw new Error("the type of cookies must be string or Map")
      }
    }
    let items = cookies.split(";");
    this.Cookies = new Map()
    items.forEach((value,index)=>{
      if(value === undefined || value === "") return;
      let keyAndValue = value.split("=");
      if(keyAndValue.length<2){
        log.warn(mysApiMagr.name,`keyAndValue lengths < 2 :${JSON.stringify(keyAndValue)}`)
        return;
      }else if(keyAndValue.length>2){
        log.warn(mysApiMagr.name,`keyAndValue lengths > 2 :${JSON.stringify(keyAndValue)}`)
      }
      this.Cookies.set(keyAndValue[0],keyAndValue[1])
    })
  }
  async getData(api: api):Promise<mysResp> {
    //处理devicefp
    // if(!(api instanceof getFp)){
    //   if(mysApiMagr.devicefp === null || mysApiMagr.devicefp === undefined){
    //     log.info(mysApiMagr.name,`missing devicefp, try to get...`)
    //     let devcefpResp = await this.getData(await getFp.getInstance())
    //     if(devcefpResp === undefined || devcefpResp === null){
    //       log.warn(mysApiMagr.name,`Failed to get devicefp.`)
    //     }else{
    //       mysApiMagr.devicefp = devcefpResp.data.device_fp;
    //       kvStoreUtil.getInstance().put("MHY-DeviceFp",mysApiMagr.devicefp)
    //     }
    //   }
    // }
    if(api instanceof dailyNoteApi){
      let devcefpResp = await this.getData(await getFp.getInstance())
      mysApiMagr.devicefp = devcefpResp.data.device_fp;
      kvStoreUtil.getInstance().put("MHY-DeviceFp",mysApiMagr.devicefp)
    }
    //组装headers
    let headers = this.getHeaders();
    if(mysApiMagr.devicefp!=undefined && mysApiMagr.devicefp!=null){
      headers["DS"] = await this.getDS(api.getBodyStr(),api.getQueryStr())
      headers["x-rpc-device_fp"] = mysApiMagr.devicefp
    }
    //组装url
    let qs = api.getQueryStr();
    let url = api.url + '?' + qs;

    //判断缓存
    let res = await this.getCache(url)
    if(res!=undefined){
      return res as mysResp
    }
    //执行请求
    let httpRequest = http.createHttp();
    log.debug(mysApiMagr.name,`[url]${url}[method]${api.method}[header]${JSON.stringify(headers)}}[body]${JSON.stringify(api.body)}`)
    res=await httpRequest.request(url,
      {
        header:headers,
        method:api.method,
        extraData:api.body
      })

    //判断请求是否成功 HTTP
    let resp = res as http.HttpResponse
    log.debug(mysApiMagr.name,`Response:${JSON.stringify(resp)}`)
    if(resp.responseCode !== http.ResponseCode.OK){
      log.warn(mysApiMagr.name,`the ResponseCode is ${resp.responseCode}, not 200. url:${url}`)
      return Promise.reject(`the HTTP CODE is ${resp.responseCode},please check your network.`)
    }
    let cookies = resp.header["set-cookie"].split(";");
    for (let value of cookies) {
      let keyAndValue = value.split("=");
      if(["HttpOnly","Path","Domain"].indexOf(keyAndValue[0].trim())!=-1) continue;
      if(keyAndValue.length<2){
        log.warn("MiHoYoLogin",`keyAndValue lengths < 2 :${JSON.stringify(keyAndValue)}`)
        continue;
      }else if(keyAndValue.length>2){
        let i=2;
        while(i<=keyAndValue.length)
          keyAndValue[1]+=keyAndValue[i++]
        log.warn("MiHoYoLogin",`keyAndValue lengths > 2 :${JSON.stringify(keyAndValue)}`)
      }
      if(keyAndValue[0].trim()!=""){
        this.Cookies.set(keyAndValue[0].trim(),keyAndValue[1].trim())
      }
    }
    //处理响应体
    let result:Object
    if(resp.resultType === http.HttpDataType.STRING){
      result = JSON.parse(resp.result.toString())
    }else if(resp.resultType === http.HttpDataType.OBJECT){
      result = resp.result
    }else{
      log.error(mysApiMagr.name,`unexpected data type: ArrayBuffer`)
      return Promise.reject(`unexpected data type: ArrayBuffer`)
    }

    //判断请求是否成功 mys
    let errorInfo = this.checkResponse(result as mysResp)
    if(errorInfo === undefined){
      this.updateCache(url,result)
      return Promise.resolve(result as mysResp)
    }
    return Promise.reject(errorInfo)
  }

  private checkResponse(resp:mysResp):string|undefined{
    switch (resp.retcode){
      case -10001: return resp.message;
      case 1034: return "遇到验证码，请稍后再试";
      case 0: return undefined;
      default:
              return resp.message
    }
  }

  private getHeaders(){
    let cookies = ""
    this.Cookies.forEach((value,key)=>{
      cookies += `${key.trim()}=${value.trim()}; `
    })
    return {
      //"Host": "api-takumi-record.mihoyo.com",
      "User-Agent": `Mozilla/5.0 (Linux; Android 13; Pixel 5;) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.131 Mobile Safari/537.36 miHoYoBBS/2.40.1`,
      // "Accept": "application/json, text/plain, */*",
      "Cookie": cookies,
      //"DS":await this.getDS(body,query),
      "Origin": "https://webstatic.mihoyo.com",
      "x-rpc-app_version": "2.40.1",
      "x-rpc-client_type": "5",
      "X-Requested-With": "com.mihoyo.hyperion"
    }
  }

  private async getDS(body:string,query:string){
    let r = this.getRandomStr(6);
    let time = await systemDateTime.getCurrentTime(false);
    let t = time.toString().slice(0,-3)
    let md = cryptoFramework.createMd("MD5");
    let str = `salt=${this.salt}&t=${t}&r=${r}&b=${body}&q=${query}`
    let encoder = new util.TextEncoder();
    let buffer = new ArrayBuffer(str.length);
    let dest = new Uint8Array(buffer);
    encoder.encodeIntoUint8Array(str,dest);
    md.update({data:dest})
    let res = await md.digest()
    let ds = `${t},${r},${this.toHexString(res.data)}`
    log.debug(mysApiMagr.name,`DS=${ds} \n salt=${this.salt} \n body=${body} \n query=${query}`)
    return ds
  }

  private getRandomStr(count:number = 6):string{
    let CONSTANTS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    let arr = []
    for(let i=1;i<=6;i++){
      arr.push(CONSTANTS[Math.round(Math.random()*CONSTANTS.length)])
    }
    return arr.join("")
  }

  /**
   * 将Uint8Array转化成HexString
   * @param bytes Uint8Array
   * @returns
   */
  private toHexString(bytes:Uint8Array):string{
    return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')
  }

}

interface mysResp{
  retcode:number,
  message:string,
  data:any
}